package com.example.myproject.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by john on 14-6-17.
 */
public class HttpInvoker {


    private static class Holder {
        private static HttpInvoker generalInstance = new HttpInvoker();
        private static HttpInvoker telecomInstance = new HttpInvoker();
        private static HttpInvoker cacheInstance = new HttpInvoker();
        private static HttpInvoker baiduInstance = new HttpInvoker();
        private static HttpInvoker weixinInstance = new HttpInvoker();
        private static HttpInvoker fangAccountInstance = new HttpInvoker();
        private static HttpInvoker roamViewInstance = new HttpInvoker();//速腾VR
        private static HttpInvoker taiwuInstance = new HttpInvoker(); //太屋
        private static HttpInvoker timerInstance = new HttpInvoker();//timer服务器
        private static HttpInvoker migrationIndexInstance = new HttpInvoker(20,10);//timer服务器
        private static HttpInvoker tradeCenterInstance = new HttpInvoker(20000);
        private static HttpInvoker apolloSiteInstance = new HttpInvoker(1,1);
        private static HttpInvoker companyLogo = new HttpInvoker(); //太屋
        private static HttpInvoker servicePlatform = new HttpInvoker();//20s连接超时
    }

    private static Log log = LogFactory.getLog(HttpInvoker.class);

    private CloseableHttpClient client;// 客户端
    private String host;// 主机地址
    private ResponseHandler<String> handler;// 应答处理
    private boolean wrap;// 是否进行Agent伪装
    private boolean hasHeader;//是否添加自定义header
    private Map<String,String> headerMap=null;//自定义头信息
    private Integer timeout = 10000;

    public HttpInvoker() {
        handler = new BasicResponseHandler();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

        // !!! make this configruable
        cm.setMaxTotal(200);//连接池最大并发连接数搜索
        cm.setDefaultMaxPerRoute(100);//单路由最大并发数


        // !!! !!! make this configruable
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(timeout)
                .setSocketTimeout(timeout)
                .setConnectTimeout(timeout)
                .build();


        client = HttpClientBuilder.create()
                .setConnectionManager(cm)
                .setDefaultRequestConfig(requestConfig)
                .build();

    }

    private HttpInvoker(int timeout) {
        handler = new BasicResponseHandler();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

        // !!! make this configruable
        cm.setMaxTotal(200);//连接池最大并发连接数搜索
        cm.setDefaultMaxPerRoute(100);//单路由最大并发数


        // !!! !!! make this configruable
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(timeout)
                .setSocketTimeout(timeout)
                .setConnectTimeout(timeout)
                .build();


        client = HttpClientBuilder.create()
                .setConnectionManager(cm)
                .setDefaultRequestConfig(requestConfig)
                .build();

    }

    /**
     * 第一行设置ConnectionRequestTimeout：这定义了从ConnectionManager管理的连接池中取出连接的超时时间，此处设置为1秒。
     第二行设置ConnectionTimeout：　　这定义了通过网络与服务器建立连接的超时时间。Httpclient包中通过一个异步线程去创建与服务器的socket连接，
     这就是该socket连接的超时时间，此处设置为2秒。
     第三行设置SocketTimeout：　　　　这定义了Socket读数据的超时时间，即从服务器获取响应数据需要等待的时间，此处设置为4秒。
     以上3种超时分别会抛出ConnectionPoolTimeoutException,ConnectionTimeoutException与SocketTimeoutException。
     *
     *
     *
     * @param maxTotal
     */
    private HttpInvoker(int maxTotal, int PerRoute) {
        handler = new BasicResponseHandler();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

        // !!! make this configruable
        cm.setMaxTotal(maxTotal);//连接池最大并发连接数搜索
        cm.setDefaultMaxPerRoute(PerRoute);//单路由最大并发数

        // !!! !!! make this configruable
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(timeout)
                .setSocketTimeout(timeout)
                .setConnectTimeout(timeout)
                .build();

        client = HttpClientBuilder.create()
                .setConnectionManager(cm)
                .setDefaultRequestConfig(requestConfig)
                .build();
    }

    /**
     *
     * @return
     */
    public static HttpInvoker getTelecomInstance() {
        return Holder.telecomInstance;
    }

    /**
     *
     * @return
     */
    public static HttpInvoker getCacheInstance() {
        return Holder.cacheInstance;
    }

    public static HttpInvoker getMigrationInstance() {
        return Holder.migrationIndexInstance;
    }


    public static HttpInvoker getFangAccountInstance(){
        return Holder.fangAccountInstance;
    }

    /**
     *
     * @return
     */
    public static HttpInvoker getBaiduInstance() {
        return Holder.baiduInstance;
    }

    /**
     *
     * @return
     */
    public static HttpInvoker getWeixinInstance() {
        return Holder.weixinInstance;
    }

    /**
     *
     * @return
     */
    public static HttpInvoker getRoamViewInstance() {
        return Holder.roamViewInstance;
    }

    public static HttpInvoker getTaiWuInstance(){
        return Holder.taiwuInstance;
    }

    public static HttpInvoker getTimerInstance(){
        return Holder.timerInstance;
    }

    public static HttpInvoker getTradeCenterInstance(){
        return Holder.tradeCenterInstance;
    }

    public static HttpInvoker getGeneralInstance(){return Holder.generalInstance;}

    public static HttpInvoker getApolloInstance(){
        return Holder.apolloSiteInstance;
    }
    public static HttpInvoker getServicePlatform() {
        return Holder.servicePlatform;
    }

    /**
     * 设置基本路径,如http://localhost:8080/report
     *
     * @param host
     */
    public HttpInvoker setHost(String host) {
        if (!host.endsWith("/")) {
            host += "/";
        }
        this.host = host;
        return this;
    }


    /**
     * 进行User-Agent伪装
     */
    public HttpInvoker setWrap(boolean wrap) {
        this.wrap = wrap;
        return this;
    }



    public HttpInvoker setTimeout(Integer timeout) {
        this.timeout = timeout;
        return this;
    }

    /**
     * 请求地址和host拼接成完整url
     *
     * @param action
     * @return
     */
    private String makeURL(String action) {
        if (action == null) {
            action = "";
        }
        if (action.startsWith("/")) {
            action = action.substring(1);
        }
        return (this.host == null ? "" : this.host) + action;
    }


    /**
     * 伪装成FF8
     *
     * @param request
     */
    private void agent(HttpRequestBase request) {
        request.setHeader("Accept",
                "text/html,application/xhtml+xml,application/xml,application/json,image/png,image/*;q=0.8,*/*;q=0.5");
        request.setHeader("Accept-Language", "zh-cn,zh;q=0.5");
        request.setHeader("User-Agent",
                "Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0");
        request.setHeader("Accept-Encoding", "gzip, deflate");
        request.setHeader("Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7");
        request.setHeader("Connection", "Keep-Alive");
    }

    private void insertHeader(HttpRequestBase request){
        if(headerMap!=null&&!headerMap.isEmpty()){
            for(String key: headerMap.keySet()){
                request.setHeader(key,headerMap.get(key));
            }
        }
    }

    public void addHeader(String key,String value){
        if(headerMap==null){
            headerMap=new HashMap<>();
        }
        headerMap.put(key, value);
    }

    /**
     * 模拟get操作
     *
     * @param action 请求地址,如果设置了host,则只需要action即可
     * @return server返回结果
     */
    public String get(String action, Map<String, Object> params) {
        List<NameValuePair> pairs = new ArrayList<NameValuePair>();
        if (params != null && !params.isEmpty()) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if(null == entry.getValue()){
                    continue;
                }

                NameValuePair nvp = new BasicNameValuePair(entry.getKey(),
                        entry.getValue().toString());
                pairs.add(nvp);
            }
        }
        try {
            //HttpResponse response = doGet(action, new UrlEncodedFormEntity(pairs, HTTP.UTF_8), requestConfig);
            return doGet(action, new UrlEncodedFormEntity(pairs, HTTP.UTF_8));
        } catch (IOException | ParseException | URISyntaxException e) {
            log.error(e.getMessage() + "@" + this.host + action, e);
        }
        return null;
    }

    /**
     * 模拟get操作
     *
     * @param action 请求地址,如果设置了host,则只需要action即可
     * @return server返回结果
     */
    public String getByString(String action, Map<String, String> params) {
        List<NameValuePair> pairs = new ArrayList<NameValuePair>();
        if (params != null && !params.isEmpty()) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                if(null == entry.getValue()){
                    continue;
                }

                NameValuePair nvp = new BasicNameValuePair(entry.getKey(),
                        entry.getValue().toString());
                pairs.add(nvp);
            }
        }
        try {
            //HttpResponse response = doGet(action, new UrlEncodedFormEntity(pairs, HTTP.UTF_8), requestConfig);
            return doGet(action, new UrlEncodedFormEntity(pairs, HTTP.UTF_8));
        } catch (IOException | ParseException | URISyntaxException e) {
            log.error(e.getMessage() + "@" + this.host + action, e);
        }
        return null;
    }

    /**
     * 模拟get
     *
     * @param action
     * @return
     */
    private String doGet(String action, HttpEntity entity) throws ParseException, URISyntaxException, IOException {
        HttpGet get = new HttpGet(makeURL(action));
        if (wrap) {
            agent(get);
        }
        if(hasHeader){
            insertHeader(get);
        }
        get.setURI(new URI(get.getURI().toString() + "?" + EntityUtils.toString(entity)));
        log.debug(get.getURI());
        //return client.execute(get);


        return client.execute(get, handler);
    }

    /**
     * 模拟post操作
     *
     * @param action 请求地址,如果设置了host,则只需要action即可
     * @param params post的参数,允许为null
     * @return server返回结果
     */
    public String post(String action, Map<String, String> params) {
        List<NameValuePair> pairs = new ArrayList<NameValuePair>();
        if (params != null && !params.isEmpty()) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                NameValuePair nvp = new BasicNameValuePair(entry.getKey(),
                        entry.getValue());
                pairs.add(nvp);
            }
        }
        try {
            return doPost(action, new UrlEncodedFormEntity(pairs, HTTP.UTF_8));
        } catch (IOException e) {
            log.error(e.getMessage() + "@" + this.host + action, e);
        }
        return null;
    }

    /**
     *
     * 可以看出，UrlEncodedFormEntity()的形式比较单一，只能是普通的键值对，局限性相对较大。
     而StringEntity()的形式比较自由，只要是字符串放进去，不论格式都可以。
     StringEntiy 可以是任何的json 格式
     * @param action
     * @param params
     * @return
     */
    public String post(String action , String params){
        try {
            return doPost(action,new StringEntity(params, "application/json", HTTP.UTF_8));
        } catch (IOException e) {
            log.error(e.getMessage() + "@" + this.host + action, e);
        }
        return null;
    }

    public String postWithHeader(String action , String params,HashMap<String,String> headerMap)throws  Exception{
        HttpPost post = new HttpPost(makeURL(action));
        if (wrap) {
            agent(post);
        }
        if(headerMap!=null){
            for(String key: headerMap.keySet()){
                post.setHeader(key,headerMap.get(key));
            }
        }
        post.setEntity(new StringEntity(params, "application/json", HTTP.UTF_8));
        return client.execute(post, new BasicResponseHandler());
    }

    /**
     * 模拟post
     *
     * @param action
     * @param entity
     * @return
     */
    private String doPost(String action, HttpEntity entity) throws IOException {
        HttpPost post = new HttpPost(makeURL(action));
        if (wrap) {
            agent(post);
        }
        if(hasHeader){
            insertHeader(post);
        }
        post.setEntity(entity);
        return client.execute(post, new BasicResponseHandler());
    }



    /**
     * 断开所有连接
     */
    public void destroy() {
        if (client != null) {
            client.getConnectionManager().shutdown();
        }
    }

    public String getHost() {
        return host;
    }

    /**
     *get a new Instance
     * @return
     * @author chains
     */
    public static HttpInvoker getInstance() {
        return new HttpInvoker();
    }

    public void setHasHeader(boolean hasHeader) {
        this.hasHeader = hasHeader;
    }

    public void setHeaderMap(Map<String, String> headerMap) {
        this.headerMap = headerMap;
    }

    public static void main(String[] args) {

        for (int i =0 ; i< 100; i++) {
            long start2 = System.currentTimeMillis();
            Map<String,String> params=new HashMap<String,String>();
            //token=e09f3f6c-b9ea-4cba-abee-b0493c5a8d9c, companyUuid=abcd5CC9-47D5-4888-B7EA-36DC98CACCF1
            params.put("token", "e09f3f6c-b9ea-4cba-abee-b0493c5a8d9c");
            params.put("companyUuid","abcd5CC9-47D5-4888-B7EA-36DC98CACCF1");
            HttpInvoker httpInvoker = HttpInvoker.getTelecomInstance();
            httpInvoker.setHost("http://c.51fxt.com/services/api/");//jms执行是只能从LoginUserUtil.getLoginUser()取值

            log.info("getNoticeInfo,httpInvoker.host:"+httpInvoker.getHost());
            log.info("getNoticeInfo,httpInvoker.params:"+params);

            String jsonResult=httpInvoker.post("getNoticeInfoFromFXT/http.do", params);
            log.info("getNoticeInfo,jsonResult:"+jsonResult);

            long end2 = System.currentTimeMillis();
            System.out.println( (end2-start2) + "ms " + jsonResult);
        }


    }
}


